Skip to content

fix(quick-router): match switch/plug/outlet tokens as whole words in home_status - #842

Merged
matedev01 merged 2 commits into
GeniePod:mainfrom
tryeverything24:fix/switch-plug-outlet-whole-words
Jul 27, 2026
Merged

fix(quick-router): match switch/plug/outlet tokens as whole words in home_status#842
matedev01 merged 2 commits into
GeniePod:mainfrom
tryeverything24:fix/switch-plug-outlet-whole-words

Conversation

@tryeverything24

@tryeverything24 tryeverything24 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

home_status_target matched its switch/plug/outlet device group with a substring contains_any, so any word merely containing those letters fired it. Because a single-word target collapses to the canonical "switches", "what is the switchboard status" confidently reported every switch and plug in the house; the multi-word cases leaked the raw utterance out as a home_status entity. Match the tokens as whole words instead. Closes #841.

utterance on main expected
what is the switchboard status home_status{entity:"switches"} abstain
is the switchgear ok home_status{entity:"switchgear ok"} abstain
is the plugin enabled home_status{entity:"plugin enabled"} abstain
is the toaster unplugged home_status{entity:"toaster unplugged"} abstain
are the earplugs in the drawer home_status{entity:"earplugs in the drawer"} abstain

Changes

One branch condition changed; every other path in home_status_target is untouched.

Real Behavior Proof

  • I have built and run the affected code locally (or noted why I could not).
  • I have verified the change end-to-end on Jetson hardware.
  • I have NOT verified on Jetson hardware, and I explain the equivalent verification path or validation gap below.

Tested profile / hardware (check all that apply):

  • jetson
  • raspberry_pi
  • portable_sbc
  • laptop
  • mac
  • CI-only / docs-only
  • Not run locally

What I ran

x86_64 Linux dev machine (laptop profile), rustc 1.94.0, branch cut from 7c67906. The changed path is pure string routing inside quick::route — no audio, Home Assistant, or hardware dependency — so route() tests exercise it end-to-end. I probed route() over the collision and genuine phrasings against unmodified main first, then wrote the regression test and confirmed it fails on main before applying the fix.

# 1. probe + repro against unmodified main
cargo test -p genie-core --lib tools::quick::tests::switch_and_outlet_status_match_whole_words_not_substrings

# 2. after the fix — the full local CI set
cargo fmt --all -- --check
RUSTFLAGS="-D warnings" cargo clippy --workspace --all-targets --locked -- -D warnings
GENIE_RUN_RELEASE_TESTS=1 cargo build --workspace --locked --all-targets
GENIE_RUN_RELEASE_TESTS=1 cargo test  --workspace --locked --all-targets
cargo test --workspace --locked --doc
cargo run --locked -p genie-ctl -- bfcl-predict-quick --cases tests/bfcl/home_tool_cases.jsonl --out target/bfcl-live-predictions.jsonl
cargo run --locked -p genie-ctl -- bfcl-score --cases tests/bfcl/home_tool_cases.jsonl --predictions target/bfcl-live-predictions.jsonl --min-strict 96
RUSTFLAGS="-D warnings" cargo clippy -p genie-core -p genie-ctl --no-default-features --all-targets --locked -- -D warnings
cargo test -p genie-core -p genie-ctl --no-default-features --all-targets --locked

What I observed

Probe on unmodified main:

what is the switchboard status  -> home_status {"entity":"switches"}
is the switchgear ok            -> home_status {"entity":"switchgear ok"}
is the plugin enabled           -> home_status {"entity":"plugin enabled"}
is the toaster unplugged        -> home_status {"entity":"toaster unplugged"}
are the earplugs in the drawer  -> home_status {"entity":"earplugs in the drawer"}

The new test fails on main:

"what is the switchboard status" must abstain, not resolve to a switch/plug/outlet status entity
test result: FAILED. 0 passed; 1 failed

After the fix: all five abstain; all six genuine phrasings return byte-identical entities to main.

Gate results on this branch:

  • cargo fmt --all -- --check — clean.
  • cargo clippy --workspace --all-targets --locked -- -D warnings — clean.
  • cargo test --workspace --locked --all-targetstools::quick 98 passed / 0 failed, genie-core lib 938 passed / 0 failed / 6 ignored, every other target green.
  • cargo test --workspace --locked --doc — green.
  • BFCL live quick-router accuracy gatestrict_accuracy: 96.15% (25/26) against the --min-strict 96 floor, the single holdout being the documented two-call multi-homework-timer case (quick-router multi-intent prompts emit a single, wrong-priority tool #533). Byte-identical to main.
  • --no-default-features clippy + test for genie-core/genie-ctl — clean / green.

On the binary_size_budget release test: it fails identically on unmodified main with my local rustc 1.94.0 (6.80 MB against the 6.8 MB budget), so it is a toolchain artifact on my box, not a regression here. I measured both sides to be sure the change does not push it further: main = 7,131,736 bytes, this branch = 7,131,128 bytes — 608 bytes smaller, since matches! over pre-split tokens compiles tighter than the six contains calls it replaces.

Jetson gap: no Jetson hardware and no aarch64 cross toolchain on this box (cargo check --target aarch64-unknown-linux-gnu stops at cc-rs: failed to find tool "aarch64-linux-gnu-gcc"), so I could not run the cross build locally — the Cross-compile (aarch64 / Jetson) CI job covers it. The change is a pure &str token comparison with no platform-dependent behaviour, no I/O, and no new dependency, so it is architecture-independent by construction.

Test plan

  1. cargo test -p genie-core --lib switch_and_outlet_status_match_whole_words_not_substrings — passes here, fails on main.
  2. cargo test -p genie-core --lib tools::quick — the other 97 router tests, including the sibling whole-word tests ice_status_matches_whole_words_not_substrings, cooktop_status_matches_whole_words_not_substrings and cover_and_gate_status_match_whole_words_not_substrings, are unchanged.

Notes for reviewers

  • I kept "outlets"/"switches"/"plugs" in the token list even though whole-word matching makes them explicit plurals rather than substrings of themselves — dropping them would silently stop matching the plural forms, which several of the genuine cases above use.
  • Scoped to this one branch. The lock/door group is the other remaining substring matcher and is already covered by fix(quick-router): match lock and door tokens as whole words in home_status #832; the two edits are in separate hunks of home_status_target and do not conflict.

Summary by CodeRabbit

  • Bug Fixes
    • Improved device-status request handling to recognize “switch,” “plug,” and “outlet” only as standalone words.
    • Prevented unrelated phrases such as “switchboard,” “unplugged,” “earplugs,” and “plugin” from being incorrectly interpreted as device-status requests.
    • Preserved correct routing for genuine switch, plug, and outlet status requests.

@github-actions github-actions Bot added the bug Something isn't working label Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd471096-72df-4f5a-983c-9e64a9a2df52

📥 Commits

Reviewing files that changed from the base of the PR and between d54e606 and e116677.

📒 Files selected for processing (1)
  • crates/genie-core/src/tools/quick.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/genie-core/src/tools/quick.rs

📝 Walkthrough

Walkthrough

The quick router now matches switch, plug, and outlet terms as whole whitespace-delimited tokens. Tests verify substring collisions abstain while genuine device-status queries continue routing correctly.

Changes

Home status routing

Layer / File(s) Summary
Whole-word matching and regression coverage
crates/genie-core/src/tools/quick.rs
clean_status_target recognizes exact switch, plug, and outlet tokens, while tests cover collision words and valid device-status queries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • #845 — Modifies related home-status target parsing in quick.rs, but addresses dryer qualifiers rather than token-boundary matching.

Possibly related PRs

Suggested reviewers: matedev01

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main behavior change: whole-word matching for switch/plug/outlet tokens in home_status.
Linked Issues check ✅ Passed The change implements whole-word matching and adds regression coverage for substring collisions while preserving valid home_status routing for #841.
Out of Scope Changes check ✅ Passed The diff appears scoped to the requested quick-router fix and its regression test, with no unrelated changes visible.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

matedev01
matedev01 previously approved these changes Jul 24, 2026

@matedev01 matedev01 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — switch/plug/outlet tokens in home_status_target now match as whole words, so 'what is the switchboard status' no longer misroutes. Same substring-safety class as the prior whole-word fixes. Verified: clippy -D warnings clean, quick-router tests (99) pass, fmt clean, live BFCL strict_accuracy 96.15% (no regression).

@matedev01

Copy link
Copy Markdown
Member

Reviewed and this looks good — approving in spirit — but it now conflicts with main in crates/genie-core/src/tools/quick.rs (several other whole-word status fixes landed just ahead of this one). Please rebase onto main and push; I'll merge once it's mergeable and green.

…home_status

The switch/plug/outlet branch of home_status_target used a substring
contains_any, so "switchboard"/"switchgear", "plugin" and
"unplugged"/"earplugs" all fired it. A single-word target collapses to
the canonical "switches", so "what is the switchboard status" confidently
reported every switch in the house, and the multi-word cases leaked the
raw utterance out as a home_status entity instead of abstaining.

Match the tokens as whole words, exactly as the sibling ice/iron/cooktop
(GeniePod#792), car (GeniePod#772), fan/fireplace (GeniePod#746) and cover/gate (GeniePod#802) groups
already do.

@matedev01 matedev01 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, rebased clean — switch/plug/outlet tokens in home_status_target now match as whole words. Verified: clippy -D warnings clean, quick-router tests (102) pass, fmt clean, live BFCL strict_accuracy 96.15% (no regression).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] quick-router: "is the plugin enabled" misroutes to home_status — switch/plug/outlet tokens matched as substrings

2 participants